package com.derbysoft.lowcostadapter.dswitch3.service.impl;

import com.derbysoft.dswitch.dto.hotel.avail.HotelAvailCriteriaDTO;
import com.derbysoft.dswitch.dto.hotel.avail.HotelAvailRS;
import com.derbysoft.dswitch.dto.hotel.avail.HotelAvailRoomStayDTO;
import com.derbysoft.dswitch.remote.hotel.dto.ErrorDTO;
import com.derbysoft.dswitch.remote.hotel.dto.HotelAvailRequest;
import com.derbysoft.dswitch.remote.hotel.dto.HotelAvailResponse;
import com.derbysoft.lowcostadapter.commons.model.Hotel;
import com.derbysoft.lowcostadapter.commons.model.RateCache;
import com.derbysoft.lowcostadapter.commons.redis.service.RateCacheService;
import com.derbysoft.lowcostadapter.commons.redis.utils.KeyValueHelper;
import com.derbysoft.lowcostadapter.commons.utils.DateTimeUtils;
import com.derbysoft.lowcostadapter.commons.utils.ErrorMessageUtils;
import com.derbysoft.lowcostadapter.dswitch2.service.RoomOccupancyService;
import com.derbysoft.lowcostadapter.dswitch2.service.RoomStayService;
import com.derbysoft.lowcostadapter.dswitch2.service.impl.StayDateRange;
import com.derbysoft.lowcostadapter.dswitch2.translator.Translator;
import com.derbysoft.lowcostadapter.dswitch3.service.AvailRequestMonitor;
import com.derbysoft.lowcostadapter.dswitch3.service.HotelProviderService;
import com.derbysoft.lowcostadapter.schedule.parse.ParseRateCacheService;
import org.apache.commons.collections.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * Created by: jason
 * Date: 2012-5-8
 */
@Service("ds3HotelCachedAvailService")
public class HotelCachedAvailServiceImpl extends BaseHotelService
        implements HotelProviderService<HotelAvailRequest, HotelAvailResponse> {

    public static final int DELAY_ONE_DAY = -24;
    private static Logger logger = LoggerFactory.getLogger(HotelCachedAvailServiceImpl.class);

    @Autowired
    private RoomOccupancyService roomOccupancyService;

    @Autowired
    private ParseRateCacheService parseRateCacheService;

    @Autowired
    private RateCacheService rateCacheRepository;

    @Autowired
    private Translator<List<RateCache>, HotelAvailResponse, HotelAvailRequest> ds3AvailResponseTranslator;

    @Autowired
    private RoomStayService<HotelAvailRequest, HotelAvailResponse> ds3RoomStayService;

    @Autowired
    private AvailRequestMonitor availRequestMonitor;

    @Override
    public HotelAvailResponse execute(HotelAvailRequest request) {
        try {
            availRequestMonitor.getQueryCacheCount().incrementAndGet();
            if (parseRateCacheService.isParsing()) {
                logger.info("parseRateCacheService is running ...");
                return null;
            }
            Hotel hotel = loadHotel(request.getHotelAvailRQ().getAvailCriteria().getHotelCodesList().get(0));
            if (isRestricted(request, hotel)) {
                availRequestMonitor.getOccupancyRestrictedCount().incrementAndGet();
                return createEmptyResult(request);
            }
            List<RateCache> rateCaches = searchRateCaches(request, hotel);
            if (CollectionUtils.isEmpty(rateCaches)) {
                return null;
            }
            availRequestMonitor.getRateHitCount().incrementAndGet();

            HotelAvailResponse response = ds3AvailResponseTranslator.translate(rateCaches, request);

            HotelAvailResponse hotelAvailResponse = ds3RoomStayService.filter(request, response, hotel);

            if (CollectionUtils.isEmpty(response.getHotelAvailRS().getHotelAvailRoomStaysList())
                    || CollectionUtils.isEmpty(response.getHotelAvailRS().getHotelAvailRoomStaysList().get(0).getRoomStaysList())) {
                availRequestMonitor.getRoomStayFilteredCount().incrementAndGet();
            }
            return hotelAvailResponse;
        } catch (Exception ex) {
            HotelAvailResponse response = new HotelAvailResponse();
            response.setTaskId(request.getHeader().getTaskId());
            ErrorDTO error = new ErrorDTO();
            error.setCode(SYSTEM);
            error.setSource(adapter);
            error.setMessage(ErrorMessageUtils.buildErrorMessage(ex));
            response.setError(error);
            return response;
        }
    }

    private List<RateCache> searchRateCaches(HotelAvailRequest request, Hotel hotel) {
        HotelAvailCriteriaDTO availCriteria = request.getHotelAvailRQ().getAvailCriteria();
        int adults = availCriteria.getRoomStayCandidate().getAdultCount();
        int children = availCriteria.getRoomStayCandidate().getChildCount();
        String key = RateCache.PREFIX + KeyValueHelper.SEPARATOR + hotel.getPropertyID() +
                KeyValueHelper.SEPARATOR + availCriteria.getStayDateRange().getCheckin() +
                KeyValueHelper.SEPARATOR + adults + KeyValueHelper.SEPARATOR + children;
        return rateCacheRepository.loadAll(key);
    }

    private HotelAvailResponse createEmptyResult(HotelAvailRequest request) {
        HotelAvailResponse hotelAvailResponse = new HotelAvailResponse();
        hotelAvailResponse.setTaskId(request.getHeader().getTaskId());
        HotelAvailRS hotelAvailRS = new HotelAvailRS();
        hotelAvailRS.setHotelAvailRoomStaysList(new ArrayList<HotelAvailRoomStayDTO>());
        hotelAvailRS.setNoAvailHotelsList(request.getHotelAvailRQ().getAvailCriteria().getHotelCodesList());
        hotelAvailResponse.setHotelAvailRS(hotelAvailRS);
        return hotelAvailResponse;
    }

    private boolean isRestricted(HotelAvailRequest request, Hotel hotel) {
        HotelAvailCriteriaDTO availCriteria = request.getHotelAvailRQ().getAvailCriteria();
        int adults = availCriteria.getRoomStayCandidate().getAdultCount();
        int children = availCriteria.getRoomStayCandidate().getChildCount();

        Date checkIn = DateTimeUtils.parse(availCriteria.getStayDateRange().getCheckin());
        Date checkOut = DateTimeUtils.addHours(DateTimeUtils.parse(availCriteria.getStayDateRange().getCheckout()), DELAY_ONE_DAY);
        StayDateRange stayDateRange = StayDateRange.build(checkIn, checkOut);
        return roomOccupancyService.isRestricted(hotel.getPropertyID(), adults, children, stayDateRange);
    }

    public void setRoomOccupancyService(RoomOccupancyService roomOccupancyService) {
        this.roomOccupancyService = roomOccupancyService;
    }

    public void setParseRateCacheService(ParseRateCacheService parseRateCacheService) {
        this.parseRateCacheService = parseRateCacheService;
    }

    public void setDs3AvailResponseTranslator(Translator<List<RateCache>, HotelAvailResponse, HotelAvailRequest> ds3AvailResponseTranslator) {
        this.ds3AvailResponseTranslator = ds3AvailResponseTranslator;
    }

    public void setDs3RoomStayService(RoomStayService<HotelAvailRequest, HotelAvailResponse> ds3RoomStayService) {
        this.ds3RoomStayService = ds3RoomStayService;
    }

    public void setAvailRequestMonitor(AvailRequestMonitor availRequestMonitor) {
        this.availRequestMonitor = availRequestMonitor;
    }
}
